package com.example.springaiollamademodemo; // 请替换为你的实际项目包名

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.List;
import java.util.Map;
import java.util.UUID;

import static org.springframework.ai.chat.client.ChatClient.*;

@SpringBootApplication // 这是一个Spring Boot应用
public class SpringAiOllamaDemoApplication {

    // 注入 ChatClient.Builder 以便我们构建不同配置的ChatClient
    @Autowired
    private ChatClient.Builder chatClientBuilder;

    // 注入我们之前定义的 ChatMemory 实例
    @Autowired
    private ChatMemory chatMemory;


    public static void main(String[] args) {
        // Spring Boot 应用的启动入口
        SpringApplication.run(SpringAiOllamaDemoApplication.class, args);
    }

    /**
     * 示例1: ChatClient 基本使用
     *
     * @param chatClient Spring 自动注入的 ChatClient 实例
     * @return CommandLineRunner，在应用启动后执行其run方法
     */
    @Bean
    public CommandLineRunner runChatClientBasic(ChatClient chatClient) {
        return args -> {
            System.out.println("--- ChatClient 基本使用：向大模型提问 ---");
            // 使用chatClient.prompt().user("你的问题").call().content() 发送问题并获取回答内容
            String responseContent = chatClient.prompt()
                    .user("解释下量子纠缠的原理")
                    .call()
                    .content(); // 获取大模型的回答内容
            System.out.println("大模型响应: " + responseContent);
            System.out.println("\n------------------------------------------------\n");
        };
    }

    /**
     * 示例2: ChatClient 多轮上下文 (设置角色)
     * 通过系统消息为大模型设定一个角色，让它的回答更符合预期。
     *
     * @param chatClient Spring 自动注入的 ChatClient 实例
     * @return CommandLineRunner
     */
    @Bean
    public CommandLineRunner runChatClientWithContext(ChatClient chatClient) {
        return args -> {
            System.out.println("--- ChatClient 多轮上下文 (设置角色): 让大模型成为科普专家 ---");
            String responseContent = chatClient.prompt()
                    .system("你是一个科普专家，擅长用通俗易懂的语言解释科学原理。") // 系统消息：设定AI角色
                    .user("讲讲黑洞是怎么形成的？") // 用户消息：实际问题
                    .call()
                    .content();
            System.out.println("大模型（科普专家）响应: " + responseContent);
            System.out.println("\n------------------------------------------------\n");
        };
    }

    /**
     * 示例3: PromptTemplate 使用
     * 通过占位符定义模板，动态填充变量来构建问题。
     *
     * @param chatClient Spring 自动注入的 ChatClient 实例
     * @return CommandLineRunner
     */

    @Bean
    public CommandLineRunner runPromptTemplate(ChatClient chatClient) {
        return args -> {
            System.out.println("--- PromptTemplate 使用：动态提问 ---");

            // 场景一：字符串模板示例 - 翻译助手
            String textToTranslate = "Hello, Spring AI is awesome!";

            // 创建模板并渲染变量
            PromptTemplate translationTemplate = new PromptTemplate("请帮我翻译这段内容：{{content}}");
            String translatedContent = chatClient.prompt()
                    .system("你是一名翻译助手，请帮助我翻译以下内容：")
                    .user(translationTemplate.render(Map.of("content", textToTranslate)))
                    .call()
                    .content();
            System.out.println("翻译结果: " + translatedContent);

            System.out.println("\n--- 另一个PromptTemplate示例：简历优化助手 ---");

            // 场景二：模拟结构化模板示例 - 简历优化助手
            String resumeContent = "我在一家大型互联网公司担任客户支持，负责日常用户咨询和故障排除。";
            PromptTemplate resumeTemplate = new PromptTemplate("请帮我优化这段内容：{{content}}");
            String optimizedResume = chatClient.prompt()
                    .system("你是一名专业的简历优化专家，请帮助我改进简历内容，使其更具吸引力。")
                    .user(resumeTemplate.render(Map.of("content", resumeContent)))
                    .call()
                    .content();
            System.out.println("优化后的简历内容: " + optimizedResume);

            System.out.println("\n------------------------------------------------\n");
        };
    }


    /**
     * 示例4: ChatClient 与 Memory 结合 (多轮对话)
     * 使用带有记忆功能的 ChatClient 进行多轮对话，AI会记住之前的上下文。
     *
     * @param chatClientWithMemory 通过 @Qualifier 注解注入我们定义的带有记忆功能的 ChatClient
     * @return CommandLineRunner
     */
    @Bean
    public CommandLineRunner runChatClientWithMemory(@Qualifier("chatClientWithMemory") ChatClient chatClientWithMemory) {
        return args -> {
            System.out.println("--- ChatClient 与 Memory 结合：实现多轮对话 ---");

            System.out.println("用户: 今天天气怎么样？");
            String response1 = chatClientWithMemory.prompt()
                    .user("今天天气怎么样？")
                    .call()
                    .content();
            System.out.println("大模型响应: " + response1);

            // 第二次提问：不直接提及地点，看大模型是否能记住上下文
            System.out.println("用户: 那我需要带伞吗？");
            String response2 = chatClientWithMemory.prompt()
                    .user("那我需要带伞吗？")
                    .call()
                    .content();
            System.out.println("大模型响应: " + response2);

            System.out.println("\n------------------------------------------------\n");
        };
    }


    /**
     * 示例5: 通过 ConversationId 管理多会话
     * 为不同的用户或场景设置独立的会话ID，即使使用同一个 ChatMemory 实例，也能保持对话独立。
     *
     * @return CommandLineRunner
     */
    @Bean
    public CommandLineRunner runChatClientWithSpecificConversationId() { // 修改方法名以避免冲突
        return args -> {
            System.out.println("--- ChatClient 与特定 ConversationId 结合  ---");

            // --- 模拟第一个用户的会话 (Alice) ---
            //String userId1 = "user-alice-" + UUID.randomUUID().toString().substring(0, 8); // 生成一个唯一的会话ID
            String userId1 = "fixed-alice-session-001";
            System.out.println("为Alice创建会话ID: " + userId1);

            // 构建一个带有特定会话ID的ChatClient
            ChatClient aliceChatClient = chatClientBuilder
                    .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory, userId1, 65536)) // 指定 ChatMemory 和 会话ID
                    .build();

            System.out.println("Alice (会话ID: " + userId1 + "): 中国的首都是哪里？");
            aliceChatClient.prompt()
                    .user("中国的首都是哪里？")
                    .call()
                    .content(); // 发送问题，会话历史存入 'user-alice-session-001'

            System.out.println("Alice (会话ID: " + userId1 + "): 它的简称是什么？");
            String responseAlice = aliceChatClient.prompt()
                    .user("它的简称是什么？")
                    .call()
                    .content();
            System.out.println("大模型响应 (Alice): " + responseAlice); // 大模型能识别“它”指代的是北京

            System.out.println("\n--- 切换到另一个用户的会话 (Bob) ---");

            String userId2 = "user-bob-" + UUID.randomUUID().toString().substring(0, 8); // 生成另一个唯一的会话ID
            System.out.println("为Bob创建会话ID: " + userId2);

            // 构建另一个带有特定会话ID的ChatClient
            ChatClient bobChatClient = chatClientBuilder
                    .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory, userId2, 65536)) // 指定 ChatMemory 和 另一个会话ID
                    .build();

            System.out.println("Bob (会话ID: " + userId2 + "): 世界上最高的山峰是哪座？");
            bobChatClient.prompt()
                    .user("世界上最高的山峰是哪座？")
                    .call()
                    .content();

            System.out.println("Bob (会话ID: " + userId2 + "): 它的海拔是多少？");
            String responseBob = bobChatClient.prompt()
                    .user("它的海拔是多少？")
                    .call()
                    .content();
            System.out.println("大模型响应 (Bob): " + responseBob); // 大模型能识别“它”指代的是珠穆朗玛峰

            System.out.println("\n------------------------------------------------\n");
        };
    }

    /**
     * 示例6: 查看对话历史 (最新修复示例)
     * 演示如何从 ChatMemory 中获取特定会话的对话历史。
     * 使用 ChatMemory 接口的 get(String conversationId) 方法。
     *
     * @return CommandLineRunner
     */
    @Bean
    public CommandLineRunner viewChatHistory() { // 修改方法名以避免冲突
        return args -> {
            System.out.println("*****##### 查看对话历史  *****#####");

            // 我们尝试查询示例5中Alice使用的固定会话ID
            String userIdToRetrieve = "fixed-alice-session-001";
            System.out.println("--- 尝试查看会话ID: " + userIdToRetrieve + " 的对话历史 ---");

            // 直接通过 ChatMemory 接口的 get(String conversationId) 方法获取消息列表
            List<Message> messagesForUser = chatMemory.get(userIdToRetrieve, 1000);

            if (messagesForUser != null && !messagesForUser.isEmpty()) {
                System.out.println("会话ID: " + userIdToRetrieve + " 的对话历史:");
                for (Message message : messagesForUser) {
                    // 打印消息类型（如 USER, AI, SYSTEM）和内容
                    System.out.println("  " + message.getMessageType().name() + ": " + message.getContent());
                }
            } else {
                System.out.println("会话ID: " + userIdToRetrieve + " 没有找到对话历史或历史为空。");
                System.out.println("请确保在运行此示例之前，`runChatClientWithSpecificConversationId` 方法已经运行，并且向此会话ID写入了消息。");
            }

            System.out.println("\n------------------------------------------------\n");
        };
    }

    /**
     * 示例7: 实战案例：构建多轮问答机器人
     * 这是一个完整的例子，展示了如何利用带记忆的 ChatClient 进行连续的、有上下文的对话。
     *
     * @param chatClientWithMemory 带有记忆功能的 ChatClient
     * @return CommandLineRunner
     */
    @Bean
    public CommandLineRunner runMultiTurnBot(@Qualifier("chatClientWithMemory") ChatClient chatClientWithMemory) {
        return args -> {
            System.out.println("--- 实战案例：构建多轮问答机器人 ---");
            System.out.println("机器人: 你好，我是基于Qwen的问答机器人，有什么可以帮助你的？");

            // 第一次对话
            String userQuestion1 = "最近有什么好看的科幻电影吗？";
            System.out.println("用户: " + userQuestion1);
            String botResponse1 = chatClientWithMemory.prompt()
                    .user(userQuestion1)
                    .call()
                    .content();
            System.out.println("机器人: " + botResponse1);

            // 第二次对话，利用上下文
            String userQuestion2 = "那其中有没有关于时间旅行的？";
            System.out.println("用户: " + userQuestion2);
            String botResponse2 = chatClientWithMemory.prompt()
                    .user(userQuestion2)
                    .call()
                    .content();
            System.out.println("机器人: " + botResponse2);

            // 第三次对话，继续利用上下文
            String userQuestion3 = "它的导演是谁？";
            System.out.println("用户: " + userQuestion3);
            String botResponse3 = chatClientWithMemory.prompt()
                    .user(userQuestion3)
                    .call()
                    .content();
            System.out.println("机器人: " + botResponse3);

            System.out.println("\n------------------------------------------------\n");
        };
    }
}